#include "vanila/compiler.h"
#include "vanila/parser.h"
#include "vanila/token.h"
#include "vanila/allocator.h"
#include <map>
#include <string>

namespace vanila
{
static Parser* parser = utils::Singleton<Parser>::instance();
static Allocator* allocator = utils::Singleton<Allocator>::instance();

Parser::Parser() : _parseSelector{
    {TokenType::LEFT_PAREN,     {SINGLETON(GroupingParser), SINGLETON(CallParser),      Precedence::CALL}},
    {TokenType::RIGHT_PAREN,    {nullptr,                   nullptr,                    Precedence::NONE}},
    {TokenType::LEFT_BRACKET,   {SINGLETON(ListParser),     SINGLETON(IndexParser),     Precedence::INDEX}},
    {TokenType::RIGHT_BRACKET,  {nullptr,                   nullptr,                    Precedence::NONE}},
    {TokenType::LEFT_BRACE,     {SINGLETON(DictParser),     nullptr,                    Precedence::NONE}},
    {TokenType::RIGHT_BRACE,    {nullptr,                   nullptr,                    Precedence::NONE}},
    {TokenType::COMMA,          {nullptr,                   nullptr,                    Precedence::NONE}},
    {TokenType::DOT,            {nullptr,                   SINGLETON(DotParser),       Precedence::CALL}},
    {TokenType::MINUS,          {SINGLETON(UnaryParser),    SINGLETON(BinaryParser),    Precedence::TERM}},
    {TokenType::PLUS,           {nullptr,                   SINGLETON(BinaryParser),    Precedence::TERM}},
    {TokenType::SEMICOLON,      {nullptr,                   nullptr,                    Precedence::NONE}},
    {TokenType::SLASH,          {nullptr,                   SINGLETON(BinaryParser),    Precedence::FACTOR}},
    {TokenType::STAR,           {nullptr,                   SINGLETON(BinaryParser),    Precedence::FACTOR}},
    {TokenType::BANG,           {SINGLETON(UnaryParser),    nullptr,                    Precedence::NONE}},
    {TokenType::BANG_EQUAL,     {nullptr,                   SINGLETON(BinaryParser),    Precedence::EQUALITY}},
    {TokenType::EQUAL,          {nullptr,                   nullptr,                    Precedence::NONE}},
    {TokenType::EQUAL_EQUAL,    {nullptr,                   SINGLETON(BinaryParser),    Precedence::EQUALITY}},
    {TokenType::GREATER,        {nullptr,                   SINGLETON(BinaryParser),    Precedence::COMPARISON}},
    {TokenType::GREATER_EQUAL,  {nullptr,                   SINGLETON(BinaryParser),    Precedence::COMPARISON}},
    {TokenType::LESS,           {nullptr,                   SINGLETON(BinaryParser),    Precedence::COMPARISON}},
    {TokenType::LESS_EQUAL,     {nullptr,                   SINGLETON(BinaryParser),    Precedence::COMPARISON}},
    {TokenType::IDENTIFIER,     {SINGLETON(VariableParser), nullptr,                    Precedence::NONE}},
    {TokenType::STRING,         {SINGLETON(StringParser),   nullptr,                    Precedence::NONE}},
    {TokenType::INTEGER,        {SINGLETON(IntegerParser),  nullptr,                    Precedence::NONE}},
    {TokenType::DECIMAL,        {SINGLETON(DecimalParser),  nullptr,                    Precedence::NONE}},
    {TokenType::AND,            {nullptr,                   SINGLETON(AndParser),       Precedence::AND}},
    {TokenType::CLASS,          {nullptr,                   nullptr,                    Precedence::NONE}},
    {TokenType::ELSE,           {nullptr,                   nullptr,                    Precedence::NONE}},
    {TokenType::FALSE,          {SINGLETON(LiteralParser),  nullptr,                    Precedence::NONE}},
    {TokenType::FOR,            {nullptr,                   nullptr,                    Precedence::NONE}},
    {TokenType::IF,             {nullptr,                   nullptr,                    Precedence::NONE}},
    {TokenType::NIL,            {SINGLETON(LiteralParser),  nullptr,                    Precedence::NONE}},
    {TokenType::OR,             {nullptr,                   SINGLETON(OrParser),        Precedence::OR}},
    {TokenType::RETURN,         {nullptr,                   nullptr,                    Precedence::NONE}},
    {TokenType::SUPER,          {SINGLETON(SuperParser),    nullptr,                    Precedence::NONE}},
    {TokenType::THIS,           {SINGLETON(ThisParser),     nullptr,                    Precedence::NONE}},
    {TokenType::TRUE,           {SINGLETON(LiteralParser),  nullptr,                    Precedence::NONE}},
    {TokenType::VAR,            {nullptr,                   nullptr,                    Precedence::NONE}},
    {TokenType::WHILE,          {nullptr,                   nullptr,                    Precedence::NONE}},
    {TokenType::ERROR,          {nullptr,                   nullptr,                    Precedence::NONE}},
    {TokenType::ENDOFFILE,      {nullptr,                   nullptr,                    Precedence::NONE}}, 
}
{}

Parser::~Parser() noexcept
{
    RELEASE_SINGLETON(UnaryParser);
    RELEASE_SINGLETON(GroupingParser);
    RELEASE_SINGLETON(IntegerParser);
    RELEASE_SINGLETON(DecimalParser);
    RELEASE_SINGLETON(LiteralParser);
    RELEASE_SINGLETON(StringParser);
    RELEASE_SINGLETON(VariableParser);
    RELEASE_SINGLETON(ThisParser);
    RELEASE_SINGLETON(SuperParser);
    RELEASE_SINGLETON(AndParser);
    RELEASE_SINGLETON(OrParser);
    RELEASE_SINGLETON(BinaryParser);
    RELEASE_SINGLETON(CallParser);
    RELEASE_SINGLETON(DotParser);
}

//! \brief parse the token which has more precedence
//! \param[in] compiler compiler object
//! \param[in] precedence specify parse precedence
void Parser::parsePrecedence(Compiler* compiler, Precedence precedence) const
{
    compiler->advanceToken();

    // get the prefix parser!
    PrefixParser* prefixParser = parser->getParseRule(compiler->_previousToken.type).prefix;
    if (prefixParser == NULL)
    {
        compiler->errorAtPreviousToken("Expect expression.");
        return;
    }

    // check wheather can assign
    bool canAssign = (precedence <= Precedence::ASSIGNMENT);
    prefixParser->parse(compiler, canAssign);

    // while until meet the lower precedence
    while (precedence <= parser->getParseRule(compiler->_currentToken.type).precedence)
    {
        compiler->advanceToken();
        InfixParser* infixParser = parser->getParseRule(compiler->_previousToken.type).infix;
        infixParser->parse(compiler, canAssign);
    }

    // f the `=` doesn’t get consumed as part of the expression, nothing else is going to consume it
    if (canAssign && compiler->matchTokenType(TokenType::EQUAL))
        compiler->errorAtPreviousToken("Invalid assignment target.");
}

//! \brief parse a unary experssion
//! \param[in] compiler
void UnaryParser::parse(Compiler* compiler, bool canAssign) const
{
    (void)canAssign;
    TokenType operatorType = compiler->_previousToken.type;

    parser->parsePrecedence(compiler, Precedence::UNARY);

    switch (operatorType)
    {
    case TokenType::BANG:   compiler->emitBytecode(OpCode::NOT); break;
    case TokenType::MINUS:  compiler->emitBytecode(OpCode::NEGATE); break;
    default: return;
    }
}

//! \brief parse a binary experssion
//! \param[in] compiler
void BinaryParser::parse(Compiler* compiler, bool canAssign) const
{
    (void)canAssign;
    TokenType operatorType = compiler->_previousToken.type;
    ParseRule rule = parser->getParseRule(operatorType);
    parser->parsePrecedence(
        compiler,
        static_cast<Precedence>(static_cast<uint8_t>(rule.precedence) + 1)
    );

    switch (operatorType)
    {
    case TokenType::PLUS:           compiler->emitBytecode(OpCode::ADD); break;
    case TokenType::MINUS:          compiler->emitBytecode(OpCode::SUBTRACT); break;
    case TokenType::STAR:           compiler->emitBytecode(OpCode::MULTIPLY); break;
    case TokenType::SLASH:          compiler->emitBytecode(OpCode::DIVIDE); break;
    case TokenType::BANG_EQUAL:     compiler->emitBytecodes(OpCode::EQUAL, OpCode::NOT); break;
    case TokenType::EQUAL_EQUAL:    compiler->emitBytecode(OpCode::EQUAL); break;
    case TokenType::GREATER:        compiler->emitBytecode(OpCode::GREATER); break;
    case TokenType::GREATER_EQUAL:  compiler->emitBytecodes(OpCode::LESS, OpCode::NOT); break;
    case TokenType::LESS:           compiler->emitBytecode(OpCode::LESS); break;
    case TokenType::LESS_EQUAL:     compiler->emitBytecodes(OpCode::GREATER, OpCode::NOT); break;
    default: return;
    }
}

//! \brief parse a grouping 
//! \param[in] compiler
void GroupingParser::parse(Compiler* compiler, bool canAssign) const
{
    (void)canAssign;
    compiler->compileExpression();
    compiler->consumeToken(TokenType::RIGHT_PAREN, "Expect ')' after expression.");
}

//! \brief parse a integer token!
//! \param[in] compiler
void IntegerParser::parse(Compiler* compiler, bool canAssign) const
{
    (void)canAssign;
    int64_t value = std::atoll(compiler->_previousToken.start);
    compiler->emitConstant(Value(value));
}

//! \brief parse a number token!
//! \param[in] compiler
void DecimalParser::parse(Compiler* compiler, bool canAssign) const
{
    (void)canAssign;
    double value = std::atof(compiler->_previousToken.start);
    compiler->emitConstant(Value(value));
}

//! \brief parse a literal token!
//! \param[in] compiler
void LiteralParser::parse(Compiler* compiler, bool canAssign) const
{
    (void)canAssign;
    switch (compiler->_previousToken.type)
    {
    case TokenType::FALSE:  compiler->emitBytecode(OpCode::FALSE); break;
    case TokenType::NIL:    compiler->emitBytecode(OpCode::NIL); break;
    case TokenType::TRUE:   compiler->emitBytecode(OpCode::TRUE); break;
    default: return;
    }
}

//! \brief parse a string token!
//! \param[in] compiler
void StringParser::parse(Compiler* compiler, bool canAssign) const
{
    (void)canAssign;
    compiler->emitConstant(Value(
        allocator->allocateString(compiler->_previousToken.start + 1, compiler->_previousToken.length - 2)
    ));
}

//! \brief parse a variable token!
//! \param[in] compiler
void VariableParser::parse(Compiler* compiler, bool canAssign) const
{
    // meet a varibale token, use the name to find it
    compiler->compileNamedVariable(compiler->_previousToken, canAssign);
}

//! \brief parse a 'and'
//! \param[in] compiler
void AndParser::parse(Compiler* compiler, bool canAssign) const
{
    // when execute the left experssion is on the top
    // if the left experssion is false, the experssion in right of 'and' is no need to execute!
    uint32_t endJump = compiler->emitJump(OpCode::JUMP_IF_FALSE);
    compiler->emitBytecode(OpCode::POP);

    parser->parsePrecedence(compiler, Precedence::AND);

    // fill JUMP_IF_FALSE jump address
    compiler->patchJump(endJump);
}

//! \brief parse a 'or'
//! \param[in] compiler
void OrParser::parse(Compiler* compiler, bool canAssign) const
{
    // when execute the left experssion is on the top
    // if the left experssion is true, the experssion in right of 'or' is no need to execute!
    // excute 'JUMP_IF_FALSE' -> no jump
    // and then meet 'JUMP' -> jump the all right expression
    // while if left experssion is false, `JUMP_IF_FALSE` whill jump the 'JUMP' bytecode, continue to evaluate
    uint32_t elsejump = compiler->emitJump(OpCode::JUMP_IF_FALSE);
    uint32_t endJump = compiler->emitJump(OpCode::JUMP);

    compiler->patchJump(elsejump);
    compiler->emitBytecode(OpCode::POP);

    parser->parsePrecedence(compiler, Precedence::OR);
    compiler->patchJump(endJump);
}

//! \brief parse a function call
//! \param[in] compiler
void CallParser::parse(Compiler* compiler, bool canAssign) const
{
    // identider ( ... 
    // parse the arguments
    uint8_t argCount = compiler->compileArgumentList();
    compiler->emitBytecodeByte(OpCode::CALL, argCount);
    // so, when vm meet OpCode::CALL, the arguments already been put in the stack
}

//! \brief parse a '.'
//! \param[in] compiler
void DotParser::parse(Compiler* compiler, bool canAssign) const
{
    compiler->consumeToken(TokenType::IDENTIFIER, "Expect property name after '.'.");
    // the identifier name after '.'
    uint8_t name = compiler->makeIdentifierConstant(compiler->_previousToken);

    // case 1: object.attr = . just set a attr value
    if (canAssign && compiler->matchTokenType(TokenType::EQUAL))
    {
        compiler->compileExpression();
        compiler->emitBytecodeByte(OpCode::SET_PROPERTY, name);
    }
    // case 2: object.fun( -> meet a '(' -> is a method call!
    else if (compiler->matchTokenType(TokenType::LEFT_PAREN))
    {
        uint8_t argCount = compiler->compileArgumentList();
        compiler->emitBytecodeByte(OpCode::INVOKE, name);
        compiler->emitByte(argCount);
    }
    // case 3: object.fun or object.attr -> get a attr or bound a method
    else
    {
        compiler->emitBytecodeByte(OpCode::GET_PROPERTY, name);
    }
}

//! \brief parse a 'this'
//! \param[in] compiler
void ThisParser::parse(Compiler* compiler, bool canAssign) const
{
    if (compiler->_currentClassEnvironment == nullptr)
    {
        compiler->errorAtPreviousToken("Can't use 'this' outside of a class.");
        return;
    }
    compiler->compileNamedVariable(compiler->_previousToken, false);
}

//! \brief parse a 'super'
//! \param[in] compiler
void SuperParser::parse(Compiler* compiler, bool canAssign) const
{
    if (compiler->_currentClassEnvironment == nullptr)
        compiler->errorAtPreviousToken("Can't use 'super' outside of a class.");
    else if (!compiler->_currentClassEnvironment->hasSuperclass)
        compiler->errorAtPreviousToken("Can't use 'super' in a class with no superclass.");

    compiler->consumeToken(TokenType::DOT, "Expect '.' after 'super'.");
    compiler->consumeToken(TokenType::IDENTIFIER, "Expect superclass method name.");

    uint8_t name = compiler->makeIdentifierConstant(compiler->_previousToken);

    // load 'this' to the stack
    compiler->compileNamedVariable(Token("this"), false);
    
    // a 'super' follow 
    // case 1: super.fun( --> meet a '(' --> a superclass's function call
    if (compiler->matchTokenType(TokenType::LEFT_PAREN))
    {
        uint8_t argCount = compiler->compileArgumentList();
        compiler->compileNamedVariable(Token("super"), false);
        compiler->emitBytecodeByte(OpCode::SUPER_INVOKE, name);
        compiler->emitByte(argCount);
    }
    // case 2: super.fun --> just bind method, not call
    else
    {
        compiler->compileNamedVariable(Token("super"), false);
        compiler->emitBytecodeByte(OpCode::GET_SUPER, name);
    }
}

//! \brief parse a '[' -> list
//! \param[in] compiler
void ListParser::parse(Compiler* compiler, bool canAssign) const
{
    uint16_t elementSize = compiler->compileList();
    compiler->emitBytecode(OpCode::LIST);
    compiler->emitShort(elementSize);
}

//! \brief parse a '{' -> dict or set
//! \param[in] compiler
void DictParser::parse(Compiler* compiler, bool canAssign) const
{
    OpCode code;
    uint16_t size = compiler->compileDictOrSet(code);
    compiler->emitBytecode(code);
    compiler->emitShort(size);
}

//! \brief parse a '[]'
//! \param[in] compiler
void IndexParser::parse(Compiler* compiler, bool canAssign) const
{
    // l[x
    compiler->compileExpression();
    compiler->consumeToken(TokenType::RIGHT_BRACKET, "Expect ']' after index or key.");
    
    // set or get?
    if (compiler->matchTokenType(TokenType::EQUAL))
    {
        compiler->compileExpression();
        // [ instance ] [ index ] [ new value ]
        compiler->emitBytecode(OpCode::SET_INDEX);
    }
    else
        // [ instance ] [ index ]
        compiler->emitBytecode(OpCode::GET_INDEX);
}

}